FHIR Servers
The platform requires at least one FHIR endpoint to handle it native FHIR resources. If you aleary have this, ensure all the platform components point to said endpoint, you may then skip deployment of default FHIR servers.
By default the platform runs two independent HAPI FHIR server instances, each with its own dedicated Helm chart:
| Instance | Module key | FHIR version | Context path | Purpose |
|---|---|---|---|---|
| ePI | fhirEpi | R5 | /epi/api | Stores electronic Product Information (ePI) FHIR resources |
| IPS | fhirIps | R4 + IPS | /ips/api | Stores International Patient Summary (IPS) resources |
Because at the time of development the official IPS was not yet specified for FHIR R5, and official ePI IG was developed for R5.
To solve this discrepancy, it was decided to deploy one R4 server for IPS, and a R5 for ePIs (and everythign else).
As consequence, the platform natively distiguishes between patient related information sources (a.k.a IPS), and non-personal resources (i.e. ePIs, and other information like Health Education Material metadata)
Both are deployed via the hapi-fhir-deployments repository and published to GHCR as OCI charts:
oci://ghcr.io/gravitate-health/charts/fhir-epioci://ghcr.io/gravitate-health/charts/fhir-ips
What each chart bundles
Each chart is self-contained:
- HAPI FHIR JPA Server — official upstream chart, no custom Docker image
- PostgreSQL — Bitnami chart, bundled as a sub-chart with its own PVC
- Istio VirtualService — routes
/epi/apior/ips/apithrough the platform gateway - Probe-patch Job — post-install/upgrade hook that corrects liveness/readiness/startup probe paths to the server's context path (upstream HAPI hardcodes
/livez//readyz) - Auto-generated DB credentials Secret — generated on first install, preserved across upgrades, retained after uninstall
Credential management
DB credentials are managed automatically by the chart using Helm's lookup function — no manual secret creation is required.
| Event | Behaviour |
|---|---|
| First install | Generates a random 32-character password; creates the Secret |
| Upgrade | Looks up the existing Secret and reuses the same password (idempotent) |
helm uninstall | Secret is kept (helm.sh/resource-policy: keep) — PVC data stays accessible |
When deployed via the helm-charts Helmfile orchestrator, the secrets are named after the release:
fhir-server-epi-postgresql(for thefhir-server-epirelease)fhir-server-ips-postgresql(for thefhir-server-ipsrelease)
To bring your own credentials instead:
kubectl create secret generic fhir-server-epi-postgresql \
--from-literal=postgres-password=<admin-pw> \
--from-literal=password=<app-pw>
Then set postgresql.auth.existingSecret=fhir-server-epi-postgresql in your values overlay — the chart will use the existing secret and skip generation.
Deployment via Helmfile (standard)
When deploying the full platform with helm-charts, both FHIR servers are included in the phase=data step and enabled by default in all profiles. No extra configuration is required beyond the standard global.host and global.gatewayName.
# Deploy only the data phase (both FHIR servers)
helmfile -f helmfile.yaml -e full -l phase=data apply
Values overlays in helm-charts/values/releases/ wire the gateway name and DB secret names automatically.
Standalone deployment
You can also deploy either chart independently from the hapi-fhir-deployments repository:
git clone https://github.com/Gravitate-Health/hapi-fhir-deployments.git
cd hapi-fhir-deployments
# Update sub-chart dependencies
helm dependency update charts/fhir-epi
helm dependency update charts/fhir-ips
# Deploy (credentials auto-generated)
helm install fhir-epi charts/fhir-epi
helm install fhir-ips charts/fhir-ips
The PostgreSQL service hostname is derived from the Helm release name. The chart default assumes the release is named fhir-epi or fhir-ips. If you use a different release name, override:
helm install my-release charts/fhir-epi \
--set hapi.externalDatabase.host=my-release-postgresql \
--set postgresql.auth.existingSecret=my-release-postgresql
When deployed via helm-charts Helmfile, the release names are fhir-server-epi and fhir-server-ips — the value overlays handle this automatically.
Probe patching — why it exists
The upstream HAPI FHIR Helm chart hardcodes probe paths to /livez and /readyz. When a server runs at a context path (e.g. /epi/api), the actual health endpoints are /epi/api/livez and /epi/api/readyz. Without correction the pod cycles NotReady indefinitely.
Both charts include a Helm hook Job (post-install, post-upgrade) that patches the Deployment probes via kubectl patch immediately after each deploy. The hook runs automatically — no manual action required.
In-cluster service URLs
All platform modules that talk to the FHIR servers use these canonical in-cluster URLs. They are the default values for the FHIR_EPI_URL / FHIR_IPS_URL environment variables across all charts:
| Variable | Default value |
|---|---|
FHIR_EPI_URL | http://fhir-server-epi:8080/epi/api/fhir |
FHIR_IPS_URL | http://fhir-server-ips:8080/ips/api/fhir |
These values are stable because both charts set hapi.fullnameOverride to fhir-server-epi / fhir-server-ips, pinning the Kubernetes Service name regardless of the Helm release name.
If you point the platform at an external FHIR endpoint, override these variables in each consuming chart. In the helm-charts helmfile you can do this globally via --state-values-set or per-module in values/releases/<module>.yaml.gotmpl:
# Example: values/releases/focusing-manager.yaml.gotmpl
config:
fhirEpiUrl: "https://my-external-fhir.example.org/epi/api/fhir"
fhirIpsUrl: "https://my-external-fhir.example.org/ips/api/fhir"
Accessing the servers
After deployment, the FHIR servers are reachable at:
| Endpoint | ePI | IPS |
|---|---|---|
| FHIR API | https://<host>/epi/api/fhir/ | https://<host>/ips/api/fhir/ |
| Readiness | https://<host>/epi/api/readyz | https://<host>/ips/api/readyz |
| Liveness | https://<host>/epi/api/livez | https://<host>/ips/api/livez |
| IPS summary | — | https://<host>/ips/api/fhir/Patient/<id>/$summary |
| Prometheus (internal) | http://fhir-server-epi:8080/actuator/prometheus | http://fhir-server-ips:8080/actuator/prometheus |
Key configuration values
All values are namespaced under hapi.* (upstream sub-chart) or chart-level keys:
| Value | ePI default | IPS default | Description |
|---|---|---|---|
hapi.image.tag | v7.6.0 | v7.4.0 | HAPI FHIR Docker image version |
hapi.replicaCount | 1 | 1 | Number of replicas |
virtualService.gateway | gh-gateway | gh-gateway | Istio Gateway resource name |
virtualService.uriPrefix | /epi/api | /ips/api | Routed URI prefix |
postgresql.auth.existingSecret | fhir-epi-postgresql | fhir-ips-postgresql | DB credentials Secret (auto-created) |
postgresql.primary.persistence.size | 8Gi | 8Gi | DB volume size |
probePatch.enabled | true | true | Run probe-patch Job on install/upgrade |
Full configuration reference: hapi-fhir-deployments README.